﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace DynamicCall
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            CallCode();
            Console.WriteLine();
            CallDll();
        }

        private static void CallDll(string strPath = "DynamicDll/DynamicClass.dll", string strClassName = "DynamicClass.TestTask", string strClassMethod = "DoTask")
        {
            Console.WriteLine("调用dll方法");
            Assembly assembly = Assembly.LoadFrom(strPath);
            Type type = assembly.GetType(strClassName);

            bool isTask = CheckTask(type);
            if (!isTask)
            {
                Console.WriteLine("动态DLL未实现接口");
                return;
            }

            object obj = Activator.CreateInstance(type);
            var result = type.InvokeMember(strClassMethod, BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, null);
            if (result != null)
                Console.WriteLine($"执行结果:{(bool)result}");
        }

        private static void CallCode(string strPath = "DynamicCode/TestTask.cs", string strClassName = "DynamicClass.TestTask", string strClassMethod = "DoTask")
        {
            Console.WriteLine("调用cs代码方法");
            string strText = File.ReadAllText(strPath);
            Assembly assembly = GenerateProxyTree(strText);
            Type type = assembly.GetType(strClassName);

            bool isTask = CheckTask(type);
            if (!isTask)
            {
                Console.WriteLine("动态代码未实现接口");
                return;
            }

            object obj = Activator.CreateInstance(type);
            var result = type.InvokeMember(strClassMethod, BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, null);
            if (result != null)
                Console.WriteLine($"执行结果:{(bool)result}");
        }

        private static bool CheckTask(Type currentType)
        {
            return currentType.GetInterfaces().Any(t => t.Name.Equals(nameof(DynamicBase.IWorkTask)));
        }

        private static Assembly GenerateProxyTree(string code)
        {
            Assembly assembly = null;
            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
            string assemblyName = Path.GetRandomFileName();

            var paths = AppDomain.CurrentDomain.BaseDirectory;
            var dlls = Directory.GetFiles(paths, "*.dll");
            string dllName = string.Empty;
            foreach (var dll in dlls)
            {
                dllName = dll.Replace(paths, "").Replace(".dll", "");
                AppDomain.CurrentDomain.Load(dllName);
            }

            var references = AppDomain.CurrentDomain.GetAssemblies().Select(x => MetadataReference.CreateFromFile(x.Location)).OrderBy(x => x.FilePath);
            CSharpCompilation compilation = CSharpCompilation.Create(assemblyName, new[] { syntaxTree }, references, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
            using var ms = new MemoryStream();
            EmitResult result = compilation.Emit(ms);
            if (result.Success)
            {
                ms.Seek(0, SeekOrigin.Begin);
                assembly = Assembly.Load(ms.ToArray());
            }
            else
            {
                IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
                                        diagnostic.IsWarningAsError ||
                                        diagnostic.Severity == DiagnosticSeverity.Error);
                foreach (Diagnostic diagnostic in failures)
                {
                    Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
                }
            }

            return assembly;
        }
    }
}